package ccm.component.oscilloscope;

import ccm.common.Buffer;
import ccm.common.Crc;
import ccm.common.ReceiveDataEvent;
import ccm.common.ReceiveDataListener;

import java.util.Arrays;
import java.util.LinkedList;

public class ScnsDataManager extends OscilloscopeDataManager implements ReceiveDataListener
{
    protected final byte[] receiveBuf;
    protected final LinkedList<TimeNameAndData> tmpLinkList;
    protected final int column;
    protected int receiveBufStart;
    protected int previousBufStart;

    public ScnsDataManager()
    {
        this(24);
    }

    public ScnsDataManager(int column)
    {
        super();
        receiveBuf=new byte[10240];
        tmpLinkList=new LinkedList<>();
        this.column=column;
        clear();
    }

    @Override
    public void clear()
    {
        super.clear();
        synchronized(receiveBuf)
        {
            receiveBufStart=0;
            previousBufStart=0;
            Arrays.fill(receiveBuf,(byte)0);
            tmpLinkList.clear();
        }
    }

    @Override
    public void setFastUpdate(boolean fastUpdate)
    {
        super.setFastUpdate(fastUpdate);
        if(fastUpdate)
        {
            synchronized(receiveBuf)
            {
                if(!tmpLinkList.isEmpty())
                {
                    if(((tmpLinkList.getLast().getTime()-tmpLinkList.getFirst().getTime())>=4*1000*1000)||tmpLinkList.size()>=65536)
                    {
                        dataList.addAll(tmpLinkList);
                        tmpLinkList.clear();
                    }
                }
            }
        }
    }

    /**
     * 获取一共有几列
     *
     * @return 列数
     */
    @Override
    public int getDataColumn()
    {
        return this.column;
    }

    interface DecodeData
    {
        float decode(byte[] buf,int i);
    }

    private void decode(final int tailLength,final int ceilSize,final int currentLen,final long us,DecodeData decodeData,int bias)
    {
        final int dataN=Buffer.getUint8(receiveBuf,receiveBufStart-tailLength);
        final int totalLen=tailLength+dataN*ceilSize;
        if(totalLen<=currentLen&&dataN<=getDataColumn()&&Buffer.getUint16(receiveBuf,receiveBufStart-5)==Crc.crc16Modbus(receiveBuf,receiveBufStart-totalLen,totalLen-5))
        {
            final long timeOrigin=Buffer.getUint32(receiveBuf,receiveBufStart-tailLength+1);
            final int nameHash=Buffer.getUint8(receiveBuf,receiveBufStart-tailLength+4);
            final long time=(timeOrigin&0X7FFFFF)|(us-(((us&0X800000L)!=(timeOrigin&0X800000L))?0X800000L:0L))&0XFFFFFFFFFF800000L;
            float[] ans=new float[dataN];
            for(int i=0;i<ans.length;++i)
            {
                ans[i]=decodeData.decode(receiveBuf,receiveBufStart-totalLen+i*ceilSize)+bias;
            }
            tmpLinkList.add(new TimeNameAndData(time,ans,nameHash));
            previousBufStart=receiveBufStart;
        }
    }

    /**
     * 接收到数据事件
     */
    @Override
    public void receiveData(ReceiveDataEvent event)
    {
        synchronized(receiveBuf)
        {
            for(byte datum: event.getData())
            {
                receiveBuf[receiveBufStart]=datum;
                receiveBufStart=(receiveBufStart+1)%receiveBuf.length;
                final int type=Buffer.getUint8(receiveBuf,receiveBufStart-1);
                final int currentLen=(receiveBufStart-previousBufStart+receiveBuf.length)%receiveBuf.length;
                if(Buffer.getUint8(receiveBuf,receiveBufStart-3)==0X00&&Buffer.getUint8(receiveBuf,receiveBufStart-2)==0X80&&type>=0X79&&type<=0X7F)
                {
                    switch(type)
                    {
                        case 0X7F ->
                        {
                            System.out.println("JustFloatDataManager receiveData 0X7F float");
                            decode((1+3+1+2+3),4,currentLen,event.getTimeUs(),Buffer::getFloat,0);
                        }
                        case 0X7E ->
                        {
                            System.out.println("JustFloatDataManager receiveData 0X7E int16 bias");
                            final int tailLength=(1+3+1+4+2+3);
                            final int bias=Buffer.getInt32(receiveBuf,receiveBufStart-tailLength+5);
                            decode(tailLength,2,currentLen,event.getTimeUs(),Buffer::getUint16,bias);
                        }
                        case 0X7D ->
                        {
                            System.out.println("JustFloatDataManager receiveData 0X7D name");
                            final int tailLength=(1+1+1+1+2+3);
                            final int len=Buffer.getUint8(receiveBuf,receiveBufStart-tailLength+3);
                            final int totalLen=tailLength+len;
                            if(totalLen<=currentLen&&Buffer.getUint16(receiveBuf,receiveBufStart-5)==Crc.crc16Modbus(receiveBuf,receiveBufStart-totalLen,totalLen-5))
                            {
                                final int hash=Buffer.getUint8(receiveBuf,receiveBufStart-tailLength);
                                final int curCh=Buffer.getUint8(receiveBuf,receiveBufStart-tailLength+1);
                                final int totCh=Buffer.getUint8(receiveBuf,receiveBufStart-tailLength+2);
                                final String name=Buffer.getString(receiveBuf,receiveBufStart-totalLen,len);
                                if(curCh<totCh)
                                {
                                    String[] split=nameMap.get(hash);
                                    if(split==null||split.length!=totCh)
                                    {
                                        split=new String[totCh];
                                    }
                                    split[curCh]=name;
                                    nameMap.put(hash,split);
                                    previousBufStart=receiveBufStart;
                                }
                            }
                        }
                        case 0X7C ->
                        {
                            System.out.println("JustFloatDataManager receiveData 0X7C int16");
                            decode((1+3+1+2+3),2,currentLen,event.getTimeUs(),Buffer::getInt16,0);
                        }
                        case 0X7B ->
                        {
                            System.out.println("JustFloatDataManager receiveData 0X7B int32");
                            decode((1+3+1+2+3),4,currentLen,event.getTimeUs(),Buffer::getInt32,0);
                        }
                        case 0X7A ->
                        {
                            System.out.println("JustFloatDataManager receiveData 0X7A float24");
                            decode((1+3+1+2+3),3,currentLen,event.getTimeUs(),Buffer::getFloat24,0);
                        }
                        case 0X79 ->
                        {
                            System.out.println("JustFloatDataManager receiveData 0X79 float16");
                            decode((1+3+1+2+3),2,currentLen,event.getTimeUs(),Buffer::getFloat16,0);
                        }
                    }
                }
            }
            if(!tmpLinkList.isEmpty()&&(fastUpdate||((tmpLinkList.getLast().getTime()-tmpLinkList.getFirst().getTime())>=300*1000)||tmpLinkList.size()>=65536))
            {
                dataList.addAll(tmpLinkList);
                tmpLinkList.clear();
            }
        }
    }
}
